package com.example.springboot.controller.download;

import cn.hutool.http.HttpUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import org.apache.commons.io.IOUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @Author:ChenZhangKun
 * @Date: 2023/10/17 20:46
 */
@RestController
@RequestMapping("/download")
public class DownloadController {
    /**
     * 单文件下载
     * 不限制下载速度
     *
     * @param response
     * @throws IOException
     */
    @GetMapping("/single")
    public void downloadSingle(HttpServletResponse response) throws IOException {
        File file = new File("E:\\hunsha\\hunsha.mp4");
        response.setContentType("video/mp4;charset=utf8");
        response.setHeader("Content-Disposition", "attachment;filename=" + file.getName());
        response.setContentLengthLong(file.length());
        try (InputStream inputStream = new FileInputStream(file);
             ServletOutputStream outputStream = response.getOutputStream()) {
            IOUtils.copy(inputStream, outputStream);
        }
    }

    /**
     * 单文件下载
     * 限制下载速度
     *
     * @param speed
     * @param response
     * @throws IOException
     */
    @GetMapping("/single/speed")
    public void downloadSingleWithSpeed(@RequestParam(value = "speed", defaultValue = "1024") Integer speed,
                                        HttpServletResponse response) throws IOException {
        File file = new File("E:\\hunsha\\hunsha.mp4");
        response.setContentType("video/mp4;charset=utf8");
        response.setHeader("Content-Disposition", "attachment;filename=" + file.getName());
        try (
                InputStream inputStream = new FileInputStream(file);
                OutputStream outputStream = response.getOutputStream()
        ) {
            byte[] buffer = new byte[1024];
            SpeedLimiter speedLimiter = new SpeedLimiter(speed);
            int length;
            while ((length = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, length);
                speedLimiter.delayNextBytes(length);
            }
        }
    }

    /**
     * zip打包下载
     *
     * @param response
     * @throws IOException
     */
    @GetMapping("/")
    public void downloadZip(HttpServletResponse response) throws IOException {
        File file = new File("E:\\hunsha\\hunsha.mp4");
        File file1 = new File("E:\\hunsha\\hunsha1.wmv");
        List<File> files = Arrays.asList(file1, file);
        response.setContentType("application/zip");
        response.setHeader("Content-Disposition", "attachment;filename=demo.zip");
        try (ZipOutputStream zip = new ZipOutputStream(response.getOutputStream())) {
            zip.setLevel(0);
            // 打包
            files.forEach(item -> {
                try (FileInputStream fileInputStream = new FileInputStream(item)) {
                    zip.putNextEntry(new ZipEntry(item.getName()));
                    IOUtils.copy(fileInputStream, zip);
                    zip.closeEntry();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
            zip.flush();
            zip.finish();
        }
    }

    /**
     * 文件夹打包成zip下载
     *
     * @param response
     * @throws Exception
     */
    @GetMapping("/folder/zip")
    public void downloadFolder(HttpServletResponse response) throws Exception {
        File file = new File("E:\\java资料\\Java数据结构\\03.Java数据结构与算法");
        response.setContentType("application/zip");
        response.setHeader("Content-Disposition", "attachment;filename=demo.zip");
        try (ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream())) {
            zipOutputStream.setLevel(0);
            zip(zipOutputStream, "", file);
            zipOutputStream.flush();
            zipOutputStream.finish();
        }

    }

    /**
     * 根据文件地址下载文件
     * 打包成zip
     *
     * @param response
     * @throws IOException
     */
    @GetMapping("/urlZip")
    public void urlZip(HttpServletResponse response) throws IOException {
        List<String> urls = Arrays.asList("https://demo.com/11666832527556.jpeg",
                "https://demo.com/11666831385156.jpeg",
                "https://demo.com/11666829917700.jpeg",
                "https://demo.com/11666762702021.png",
                "https://demo.com/11666762702020.webp",
                "https://demo.com/11666549651972.jpg",
                "https://demo.com/11666524497476.jpeg",
                "https://demo.com/11666507113092.jpg");
        response.setContentType("application/zip");
        response.setHeader("Content-Disposition", "attachment;filename=demo.zip");
        try (ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream())) {
            zipOutputStream.setLevel(0);
            urls.forEach(f -> {
                try {
                    zipOutputStream.putNextEntry(new ZipEntry(StringUtils.getFilename(f)));
                    HttpUtil.download(f, zipOutputStream, false);
                    zipOutputStream.closeEntry();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            zipOutputStream.flush();
            zipOutputStream.finish();
        }
    }

    /**
     * 生成多个excel文件打包成zip进行下载
     *
     * @param response
     * @throws IOException
     */
    @GetMapping("/excelZip")
    public void excelZip(HttpServletResponse response) throws IOException {
        response.setContentType("application/zip");
        response.setHeader("Content-Disposition", "attachment;filename=demo.zip");
        try (ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream())) {
            zipOutputStream.setLevel(0);
            for (int i = 0; i < 10; i++) {
                zipOutputStream.putNextEntry(new ZipEntry(String.format("%s.xls", i)));
                try (ExcelWriter writer = ExcelUtil.getWriter()) {
                    writer.write(generateData());
                    writer.flush(zipOutputStream);
                }
            }
            zipOutputStream.flush();
            zipOutputStream.finish();
        }
    }

    private List<List<Integer>> generateData() {
        List<List<Integer>> rows = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            rows.add(IntStream.range(i, i + 100).boxed().collect(Collectors.toList()));
        }
        return rows;
    }

    /**
     * 递归打包文件
     *
     * @param zipOutputStream
     * @param parentPath
     * @param file
     * @throws IOException
     */
    private void zip(ZipOutputStream zipOutputStream, String parentPath, File file) throws IOException {
        if (file.isDirectory()) {
            File[] subFiles = file.listFiles();
            if (subFiles != null) {
                for (File f : subFiles) {
                    zip(zipOutputStream, parentPath + file.getName() + "/", f);
                }
            }
        } else {
            try (FileInputStream fileInputStream = new FileInputStream(file)) {
                zipOutputStream.putNextEntry(new ZipEntry(parentPath + file.getName()));
                IOUtils.copy(fileInputStream, zipOutputStream);
            }
        }
    }

    public static class SpeedLimiter {
        /**
         * 速度上限(KB/s), 0=不限速
         */
        private int maxRate = 1024;

        private long getMaxRateBytes() {
            return this.maxRate * 1024L;
        }

        private long getLessCountBytes() {
            long lcb = getMaxRateBytes() / 10;
            if (lcb < 10240) lcb = 10240;
            return lcb;
        }

        public SpeedLimiter(int maxRate) {
            this.setMaxRate(maxRate);
        }

        public synchronized void setMaxRate(int maxRate) {
            this.maxRate = Math.max(maxRate, 0);
        }

        private long totalBytes = 0;
        private long tmpCountBytes = 0;
        private final long lastTime = System.currentTimeMillis();

        public synchronized void delayNextBytes(int len) {
            if (maxRate <= 0) return;
            totalBytes += len;
            tmpCountBytes += len;
            //未达到指定字节数跳过...
            if (tmpCountBytes < getLessCountBytes()) {
                return;
            }
            long nowTime = System.currentTimeMillis();
            long sendTime = nowTime - lastTime;
            long workTime = (totalBytes * 1000) / getMaxRateBytes();
            long delayTime = workTime - sendTime;
            if (delayTime > 0) {
                try {
                    Thread.sleep(delayTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                tmpCountBytes = 0;
            }
        }
    }
}
